#include <dspSimulator/instruction/stdw.h>

bool STDW::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void STDW::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                             std::shared_ptr<llvm::IRBuilder<>> irBuilder) {

}

std::shared_ptr<Instruction> STDW::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<STDW> stdw;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<5>(data, 2, 0b10001) && checkBits<1>(data, 8, 0b1) && existStandardCondition(data)) {
            stdw = std::make_shared<STDW>(dspInstInfo->getAddress(), baseCycle);
            stdw->creg_z = extractBits<4>(data, 28);
            stdw->y = data[7];
            if (stdw->y == 0) {
                stdw->funcUnit = FuncUnit::D1;
            } else {
                stdw->funcUnit = FuncUnit::D2;
            }
            stdw->mode = extractBits<4>(data, 9).to_ulong();
            stdw->offsetR = extractBits<5>(data, 13).to_ulong();
            stdw->cst = stdw->offsetR;
            stdw->baseR = extractBits<5>(data, 18).to_ulong();
            stdw->src1 = extractBits<5>(data, 23).to_ulong();
            stdw->s = data[1];
            stdw->p = data[0];
            stdw->instFormat = InstFormat::Kind1;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (checkBits<2>(data, 14, 0b10) && data[11] == 0 && checkBits<7>(data, 0, 0b1110111)) {
            stdw = std::make_shared<STDW>(dspInstInfo->getAddress(), baseCycle);
            stdw->isCompactInstruction = true;
            stdw->compactInstKind = "Dpp";
            // 基址寄存器选择B15
            stdw->baseR = 15;
            stdw->y = 1;
            // offset
            stdw->cst = data[13];
            stdw->cst += 1;
            // if t(s) == 1,src为BReg,否则src为AReg
            stdw->s = data[12];
            stdw->src1 = stdw->dst = extractBits<4>(data, 7).to_ulong();
            stdw->funcUnit = FuncUnit::D2;
            // *B15--[ucst]
            stdw->mode = 0b1010;
            stdw->RS = false;
            stdw->SAT = dspInstInfo->getSAT();
            stdw->DSZ = dspInstInfo->getDSZ();
            stdw->BR = dspInstInfo->getBR();
            stdw->PROT = dspInstInfo->getPROT();
            stdw->p = dspInstInfo->getP();
            fixUpRegOnCompactInstruction();
            stdw->fixUpRegOnCompactInstruction();
            stdw->instFormat = InstFormat::Kind1;
        } else if (dspInstInfo->getDSZ()[2] && checkBits<2>(data, 14, 0b00) && checkBits<3>(data, 9, 0b110) &&
                   checkBits<2>(data, 1, 0b10) && checkBits<2>(data, 3, 0b00)) {
            stdw = std::make_shared<STDW>(dspInstInfo->getAddress(), baseCycle);
            stdw->isCompactInstruction = true;
            stdw->compactInstKind = "DincDW";
            stdw->cst = data[13];
            stdw->cst += 1;
            stdw->s = data[12];
            stdw->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            stdw->src1 = stdw->dst = extractBits<2>(data, 5).to_ulong() << 1;
            stdw->y = data[0];
            if (stdw->y == 0) {
                stdw->funcUnit = FuncUnit::D1;
            } else {
                stdw->funcUnit = FuncUnit::D2;
            }
            stdw->mode = 0b1011; // *ptr[ucst2]++
            stdw->RS = dspInstInfo->getRS();
            stdw->SAT = dspInstInfo->getSAT();
            stdw->BR = dspInstInfo->getBR();
            stdw->DSZ = dspInstInfo->getDSZ();
            stdw->PROT = dspInstInfo->getPROT();
            stdw->p = dspInstInfo->getP();
            stdw->fixUpRegOnCompactInstruction();
            stdw->fixUpRegOnCompactInstruction();
        }
    }
    return stdw;
}

void STDW::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    auto dspMachine = std::dynamic_pointer_cast<DspMachine>(targetMachine);
    switch (executePhase) {
        case ExecutePhase::E1:
            if (instFormat == InstFormat::Kind1) {
                u32 *baseRReadPtr;
                u32 *baseRWritePtr;
                if (y == 0) {
                    baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegAName[baseR])->getDataPtr();
                    baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegAName[baseR])->getDataPtr();
                } else {
                    baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegBName[baseR])->getDataPtr();
                    baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegBName[baseR])->getDataPtr();
                }
                visitMemAddr = computeRealAddress(baseRReadPtr, baseRWritePtr, mode, 3, dspMachine);
            } else if (instFormat == InstFormat::Kind2) {
                u32 *baseRPtr;
                if (y == 0) {
                    baseRPtr = dspMachine->getSaveRegs()->getReg32ByName("B14")->getDataPtr();
                } else {
                    baseRPtr = dspMachine->getSaveRegs()->getReg32ByName("B15")->getDataPtr();
                }
                visitMemAddr = *baseRPtr + (cst << 3);
            }
            dataLo = *src1LoRegPtr;
            dataHi = *src1HiRegPtr;
            break;
        case ExecutePhase::E3:
            targetMachine->getMMU()->getMemory()->writeNBits<32>(visitMemAddr, {dataLo});
            targetMachine->getMMU()->getMemory()->writeNBits<32>(visitMemAddr + 4, {dataHi});
            break;
        default:
            break;
    }
}

std::shared_ptr<DspInstruction> STDW::clone() {
    return std::make_shared<STDW>(*this);
}

std::string STDW::toString() const {
    std::string res;
    if (isCompactInstruction) {

    } else {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)];
    switch (instFormat) {
        case InstFormat::Kind1:
            res += " " + getSrc1Reg64Name() + "," + addressModeToString();
            break;
        default:
            break;
    }
    return res;
}